package com.tonann.weixin.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tonann.common.utils.DateUtils;
import com.tonann.weixin.config.WxMaConfiguration;
import com.tonann.weixin.config.WxMaProperties;
import com.tonann.weixin.constant.ConfigConstant;
import com.tonann.weixin.constant.WxMaConstants;
import com.tonann.weixin.domain.WxUser;
import com.tonann.weixin.entity.ThirdSession;
import com.tonann.weixin.entity.WxOpenDataDTO;
import com.tonann.weixin.mapper.WxUserMapper;
import com.tonann.weixin.model.WXAccessToken;
import com.tonann.weixin.service.IWxUserService;
import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 微信用户Service业务层处理
 *
 * @author ruoyi
 * @date 2022-04-11
 */
@Service
@AllArgsConstructor
public class WxUserServiceImpl implements IWxUserService {
    private final WxUserMapper wxUserMapper;

    private final RedisTemplate redisTemplate;

    private final WxMaProperties wxMaProperties;

    /**
     * 查询微信用户
     *
     * @param id 微信用户主键
     * @return 微信用户
     */
    @Override
    public WxUser selectWxUserById(String id) {
        return wxUserMapper.selectWxUserById(id);
    }

    @Override
    public WxUser selectWxUserByOpenId(String openId) {
        return wxUserMapper.selectWxUserByOpenId(openId);
    }

    /**
     * 查询微信用户列表
     *
     * @param wxUser 微信用户
     * @return 微信用户
     */
    @Override
    public List<WxUser> selectWxUserList(WxUser wxUser) {
        return wxUserMapper.selectWxUserList(wxUser);
    }

    /**
     * 新增微信用户
     *
     * @param wxUser 微信用户
     * @return 结果
     */
    @Override
    public int insertWxUser(WxUser wxUser) {
        wxUser.setCreateTime(DateUtils.getNowDate());
        return wxUserMapper.insertWxUser(wxUser);
    }

    /**
     * 修改微信用户
     *
     * @param wxUser 微信用户
     * @return 结果
     */
    @Override
    public int updateWxUser(WxUser wxUser) {
        wxUser.setUpdateBy(wxUser.getNickName());
        wxUser.setUpdateTime(DateUtils.getNowDate());
        return wxUserMapper.updateWxUser(wxUser);
    }

    /**
     * 批量删除微信用户
     *
     * @param ids 需要删除的微信用户主键
     * @return 结果
     */
    @Override
    public int deleteWxUserByIds(String[] ids) {
        return wxUserMapper.deleteWxUserByIds(ids);
    }

    /**
     * 删除微信用户信息
     *
     * @param id 微信用户主键
     * @return 结果
     */
    @Override
    public int deleteWxUserById(String id) {
        return wxUserMapper.deleteWxUserById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WxUser loginMa(String appId, String jsCode) throws WxErrorException {
        WxMaJscode2SessionResult jscode2session = WxMaConfiguration.getMaService(appId).jsCode2SessionInfo(jsCode);
        Long startTime = WXAccessToken.getInstance().getGetTime();
        if (startTime == null || System.currentTimeMillis() / 1000 - startTime > 0) {
            JSONObject jsonObject = this.getToken(appId);
            Integer expiresTime = jsonObject.get("expires_in", Integer.class);
            String accessToken = jsonObject.get("access_token", String.class);
            WXAccessToken.getInstance().setWxAccessToken(accessToken);
            WXAccessToken.getInstance().setGetTime(System.currentTimeMillis() / 1000 + expiresTime);
        }
        WxUser wxUser = this.selectWxUserByOpenId(jscode2session.getOpenid());
        if (wxUser == null) {
            //新增微信用户
            wxUser = new WxUser();
            wxUser.setId(System.currentTimeMillis() + "");
            wxUser.setAppType(ConfigConstant.WX_APP_TYPE_1);
            wxUser.setOpenId(jscode2session.getOpenid());
            wxUser.setSessionKey(jscode2session.getSessionKey());
            wxUser.setUnionId(jscode2session.getUnionid());
            wxUser.setCreateTime(DateUtils.getNowDate());
            wxUser.setCreateBy("minipro");
            wxUser.setUpdateTime(DateUtils.getNowDate());
            wxUser.setUpdateBy("minipro");
            try {
                this.insertWxUser(wxUser);
            } catch (DuplicateKeyException e) {
                if (e.getMessage().contains("uk_appid_openid")) {
                    wxUser = this.selectWxUserByOpenId(wxUser.getOpenId());
                }
            }
        } else {
            //更新SessionKey
            wxUser.setAppType(ConfigConstant.WX_APP_TYPE_1);
            wxUser.setOpenId(jscode2session.getOpenid());
            wxUser.setSessionKey(jscode2session.getSessionKey());
            wxUser.setUnionId(jscode2session.getUnionid());
            wxUser.setUpdateTime(DateUtils.getNowDate());
            wxUser.setUpdateBy(wxUser.getNickName());
            this.updateWxUser(wxUser);
        }

        String thirdSessionKey = UUID.randomUUID().toString();
        ThirdSession thirdSession = new ThirdSession();
        thirdSession.setAppId(appId);
        thirdSession.setSessionKey(wxUser.getSessionKey());
        thirdSession.setWxUserId(wxUser.getId());
        thirdSession.setOpenId(wxUser.getOpenId());
        //将3rd_session和用户信息存入redis，并设置过期时间
        String key = WxMaConstants.THIRD_SESSION_BEGIN + ":" + thirdSessionKey;
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(thirdSession), WxMaConstants.TIME_OUT_SESSION, TimeUnit.HOURS);
        wxUser.setSessionKey(thirdSessionKey);
        wxUser.setWxSessionKey(jscode2session.getSessionKey());
        return wxUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public WxUser saveOrUpdateWxUser(WxOpenDataDTO wxOpenDataDTO) {
        WxMaUserService wxMaUserService = WxMaConfiguration.getMaService(wxOpenDataDTO.getAppId()).getUserService();
        WxMaUserInfo wxMaUserInfo = wxMaUserService.getUserInfo(wxOpenDataDTO.getSessionKey(), wxOpenDataDTO.getEncryptedData(), wxOpenDataDTO.getIv());
        WxUser wxUser = new WxUser();
        BeanUtil.copyProperties(wxMaUserInfo, wxUser);
        wxUser.setId(wxOpenDataDTO.getUserId());
        wxUser.setSex(wxMaUserInfo.getGender());
        wxUser.setHeadimgUrl(wxMaUserInfo.getAvatarUrl());
        wxUser.setUpdateTime(DateUtils.getNowDate());
        wxUser.setUpdateBy(wxMaUserInfo.getNickName());
        this.updateWxUser(wxUser);
        wxUser = this.selectWxUserById(wxUser.getId());
        return wxUser;
    }

    @Override
    public WxUser screenshot(String openId) {
        if (openId == null) {
            return null;
        }
        WxUser wxUser = selectWxUserByOpenId(openId);
        if (wxUser.getScreenshot() != null && wxUser.getScreenshot() > 3) {
            wxUser.setStatus("1");
        } else {
            wxUser.setScreenshot(wxUser.getScreenshot() == null ? 0 : wxUser.getScreenshot() + 1);
        }
        updateWxUser(wxUser);
        return wxUser;
    }


    public JSONObject getToken(String appId) {
        String result = null;
        String APPID = appId;
        String APPSECRET = null;
        List<WxMaProperties.Config> configs = wxMaProperties.getConfigs();
        if (configs != null) {
            for (WxMaProperties.Config config : configs) {
                String app = config.getAppId();
                if (app.equals(appId)) {
                    APPSECRET = config.getSecret();
                }
            }
        }
        if (APPSECRET == null) {
            return null;
        }
        // 接口调用凭证：accessToken
        String baseUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + APPSECRET;
        HttpResponse response = HttpRequest.get(baseUrl)
                .header(Header.CONTENT_ENCODING, "UTF-8")
                // 发送json数据需要设置contentType
                .header(Header.CONTENT_TYPE, "application/x-www-form-urlencoded")
                .execute();
        if (response.getStatus() == HttpStatus.HTTP_OK) {
            result = response.body();
        }
        return JSONUtil.parseObj(result);
    }
}
